package cn.sunshine.ymcc.service.impl;

import cn.sunshine.ymcc.domain.AlipayInfo;
import cn.sunshine.ymcc.domain.PayOrder;
import cn.sunshine.ymcc.dto.ApplyPayOrderDto;
import cn.sunshine.ymcc.dto.Order2PayParamDto;
import cn.sunshine.ymcc.mapper.PayOrderMapper;
import cn.sunshine.ymcc.service.IAlipayInfoService;
import cn.sunshine.ymcc.service.IPayOrderService;
import cn.sunshine.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.kernel.util.ResponseChecker;
import com.alipay.easysdk.payment.common.models.AlipayTradeCloseResponse;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Mr.Jiang
 * @since 2022-08-19
 */
@Service
@Slf4j
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {

    @Autowired
    private IAlipayInfoService alipayInfoService;
    @Override
    @Transactional
    public void savePayOrder(Order2PayParamDto order2PayParamDto) {
        String orderNo = order2PayParamDto.getOrderNo();
        //消费消息一定要幂等（只能被消费一次）
        Wrapper<PayOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no", orderNo);
        PayOrder payOrder = selectOne(wrapper);
        if (payOrder == null) {
            payOrder = new PayOrder();
            BeanUtils.copyProperties(order2PayParamDto, payOrder);
            payOrder.setCreateTime(new Date());
            payOrder.setPayStatus(PayOrder.STATE_WAIT_PAY);
            insert(payOrder);  // 不能报错，报错就是签收失败
        }
    }
/*
* @Title: checkPayOrder
* @Description: 轮询查询订单是否存在
* @Author: Mr.Jiang
* @Version: 1.0
* @Date:  2022/8/22 10:19
* @Parameters: [orderNo]
* @Return cn.sunshine.ymcc.domain.PayOrder
*/
    @Override
    public PayOrder checkPayOrder(String orderNo) {

        Wrapper<PayOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        PayOrder payOrder = selectOne(wrapper);
        return payOrder;
    }
/*
* @Title: applyPayOrder
* @Description: 调用支付宝支付
* @Author: Mr.Jiang
* @Version: 1.0
* @Date:  2022/8/22 11:41
* @Parameters: [applyPayOrderDto]
* @Return java.lang.String
*/
    @Override
    public String applyPayOrder(ApplyPayOrderDto applyPayOrderDto) {
    // 1.参数校验（已经校验JSR）
        // 2.业务校验
        // 校验支付单是否存在
        PayOrder payOrder = checkPayOrder(applyPayOrderDto.getOrderNo());
        AssertUtil.isNotNull(payOrder,"支付单不存在");
        // 若存在校验支付单状态是不是待支付
        boolean b = payOrder.getPayStatus() == PayOrder.STATE_WAIT_PAY;
        AssertUtil.isTrue(b,"支付状态不对");
        // 3.若是上面都没问题就直接调用支付宝支付（这里需要选择所调用的支付方式，看前端是传的什么）
        if(payOrder.getPayType().intValue() == 1){ // 支付方式:0余额直接，1支付宝，2微信,3银联
            // 这里是调用什么支付方式就返回对应的form表单
           return zhiFuBaoPay(payOrder,applyPayOrderDto);
        }else if (payOrder.getPayType().intValue() == 2){
            // 调用微信
            return null;
        }
            return null;
    }
/*
* @Title: cancelPayOrderByPayTimeOut
* @Description: 支付超时取消支付单
* @Author: Mr.Jiang
* @Version: 1.0
* @Date:  2022/8/22 21:25
* @Parameters: [orderNo]
* @Return void
*/
    @Override
    public void cancelPayOrderByPayTimeOut(String orderNo) {
        // 0.查询订单存在否
        PayOrder payOrder = checkPayOrder(orderNo);
        if(payOrder == null){
            return;
        }
        if(payOrder.getPayStatus() != PayOrder.STATE_WAIT_PAY){
            return;
        }
        //1.取消支付单
        payOrder.setPayStatus(PayOrder.STATE_CANCEL);
        payOrder.setUpdateTime(new Date());
        updateById(payOrder);
        //2.调用支付宝关闭交易
        try {
            AlipayTradeCloseResponse close = Factory.Payment.Common().close(orderNo); // Common().cannel没有用。用close
            log.info("调用支付宝关闭交易：{}", JSON.toJSONString(close));
            //2.1 关闭成功
            //2.2 关闭失败 notify的时候走退款
        } catch (Exception e) {
            e.printStackTrace();
        }


    }

    public String zhiFuBaoPay(PayOrder payOrder, ApplyPayOrderDto applyPayOrderDto)  {
        // 1. 设置参数（全局只需设置一次）
        AlipayInfo alipayInfo = alipayInfoService.selectList(null).get(0);
        Factory.setOptions(getOptions(alipayInfo));
        try {
           /* // 2. 发起API调用（以创建当面付收款二维码为例）
            AlipayTradePrecreateResponse response = Payment.FaceToFace()
                    .preCreate("Apple iPhone11 128G", "2234567890", "5799.00");*/
            // 2.网页支付
            AlipayTradePagePayResponse response = Factory.Payment.Page().pay(
                    payOrder.getSubject(),
                    payOrder.getOrderNo(),
                    payOrder.getAmount().toString(),
                    // 这里的意思就是当你前端传过来的CallUrl为空那么我就使用数据库的，否则就使用你前端传过来的CallUrl
                    StringUtils.isEmpty(applyPayOrderDto.getCallUrl()) ? alipayInfo.getReturnUrl():applyPayOrderDto.getCallUrl());


            // 3. 处理响应或异常
            if (ResponseChecker.success(response)) {
                System.out.println(response.getBody());
               // 这里调用成功应该返回一个form表单回去
                return response.getBody();

            } else {
                System.err.println("调用失败，原因：" + response);
            }
        } catch (Exception e) {
            System.err.println("调用遭遇异常，原因：" + e.getMessage());
            throw new RuntimeException(e.getMessage(), e);
        }
        return null;
    }
    public Config getOptions(AlipayInfo alipayInfo) {
        // 下面的参数不能写死，直接去数据库查相关的支付信息（比如支付宝，微信等）
        Config config = new Config();
        config.protocol = alipayInfo.getProtocol();
        config.gatewayHost = alipayInfo.getGatewayHost();
        config.signType = alipayInfo.getSignType();
        config.appId = alipayInfo.getAppId();
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = alipayInfo.getMerchantPrivateKey();

        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = alipayInfo.getAlipayPublicKey();
        //可设置异步通知接收服务地址（可选）
        config.notifyUrl = alipayInfo.getNotifyUrl();

        return config;
    }
}
